package com.cheese.everything;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class EverythingApplication extends Application {

    private static final int SINGLE_INSTANCE_PORT = 9527;
    private static ServerSocket instanceLock;
    // 线程池
    private final ExecutorService executor = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2,
            60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadFactory() {
                private int counter = 0;
                @Override
                public Thread newThread(Runnable r) {
                    Thread t = new Thread(r, "search-worker-" + counter++);
                    t.setDaemon(true);
                    return t;
                }
            },
            new ThreadPoolExecutor.DiscardPolicy()
    );
    private final AtomicBoolean isSearching = new AtomicBoolean(false);
    // 查询结果集
    private final ObservableList<FileResult> fileResults = FXCollections.observableArrayList();
    // 分类展示的结果集
    private FilteredList<FileResult> filteredResults;
    private Map<String, Tab> fileTypeTabs = new HashMap<>();
    private TabPane tabPane;
    private final TableView<FileResult> tableView = new TableView<>(fileResults);
    private final TextField searchField = new TextField();
    private final ProgressBar progressBar = new ProgressBar(0);
    private final Label statusLabel = new Label("就绪");
    private Task<Void> currentSearchTask;


    public static void main(String[] args) {
        // 检查是否已运行（禁止多开）
        if (!checkSingleInstance()) {
            Alert alert = new Alert(Alert.AlertType.ERROR, "程序已在运行，不支持多开！");
            alert.showAndWait();
            return;
        }
        launch(args);
    }

    /**
     * 单端口禁止多开
     * @return 检查结果
     */
    private static boolean checkSingleInstance() {
        try {
            instanceLock = new ServerSocket(SINGLE_INSTANCE_PORT);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("文件检索工具");
        primaryStage.setMinWidth(1210);
        primaryStage.setMinHeight(600);
        // 进程关闭资源销毁监听
        primaryStage.setOnCloseRequest(e -> {
            executor.shutdownNow();
            if (instanceLock != null) {
                try { instanceLock.close(); } catch (IOException ignored) {}
            }
        });

        BorderPane root = new BorderPane();
        root.getStyleClass().add("root-container");

        // 添加CSS样式表
        Scene scene = new Scene(root, 1210, 700);
        scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/styles.css")).toExternalForm());

        // 创建标签页
        createFileTabs();
        tabPane.setSide(Side.LEFT); // 设置标签页在左侧显示
        tabPane.setRotateGraphic(false);

        root.setTop(createTopPanel());
        root.setCenter(tabPane);
        root.setBottom(createStatusPanel());

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void createFileTabs() {
        tabPane = new TabPane();
        tabPane.setSide(Side.LEFT);
        tabPane.getStyleClass().add("file-type-tabs");
        tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); // 禁止关闭标签

        // 初始化过滤列表
        filteredResults = new FilteredList<>(fileResults, p -> true);

        // 创建"全部"标签
        Tab allTab = new Tab("全部");
        allTab.setContent(createTableView(fileResults));
        tabPane.getTabs().add(allTab);
        fileTypeTabs.put("全部", allTab);

        // 创建各类型文件标签
        Map<String, String> typeMappings = new LinkedHashMap<>();
        typeMappings.put("Excel", "Excel表格");
        typeMappings.put("Word", "Word文档");
        typeMappings.put("PPT", "PPT演示文稿");
        typeMappings.put("PDF", "PDF文件");
        typeMappings.put("图片", "图片文件");
        typeMappings.put("视频", "视频文件");
        typeMappings.put("音频", "音频文件");
        typeMappings.put("压缩", "压缩文件");

        // 为每种类型创建标签和过滤列表
        for (Map.Entry<String, String> entry : typeMappings.entrySet()) {
            String tabName = entry.getKey();
            String typeKeyword = entry.getValue();

            FilteredList<FileResult> typeFiltered = new FilteredList<>(fileResults,
                    file -> file.getType().contains(typeKeyword));

            Tab typeTab = new Tab(tabName);
            typeTab.setContent(createTableView(typeFiltered));
            tabPane.getTabs().add(typeTab);
            fileTypeTabs.put(tabName, typeTab);
        }
    }

    private HBox createTopPanel() {
        HBox topPanel = new HBox(15);
        topPanel.getStyleClass().add("top-panel");
        topPanel.setPadding(new Insets(15));

        Button searchButton = new Button("🔍");
        searchButton.getStyleClass().addAll("search-button", "action-button");
        searchButton.setOnAction(e -> startSearch());

        searchField.setPromptText("输入搜索内容...");
        searchField.getStyleClass().add("search-field");
        searchField.setPrefWidth(300);
        searchField.setOnAction(e -> startSearch());

        topPanel.getChildren().addAll(searchField, searchButton);
        return topPanel;
    }

    private TableView<FileResult> createTableView(ObservableList<FileResult> dataSource) {
        TableView<FileResult> tableView = new TableView<>(dataSource);
        tableView.getStyleClass().add("result-table");
        tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

        // 名称列
        TableColumn<FileResult, String> nameColumn = new TableColumn<>("名称");
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
        nameColumn.setPrefWidth(250);
        nameColumn.setResizable(true);  // 允许手动调整宽度
        nameColumn.getStyleClass().add("table-column");

        // 类型列
        TableColumn<FileResult, String> typeColumn = new TableColumn<>("类型");
        typeColumn.setCellValueFactory(new PropertyValueFactory<>("type"));
        typeColumn.setPrefWidth(100);
        typeColumn.setResizable(true);  // 允许手动调整宽度
        typeColumn.getStyleClass().add("table-column");

        // 路径列（可拉伸，占更多宽度）
        TableColumn<FileResult, String> pathColumn = new TableColumn<>("路径");
        pathColumn.setCellValueFactory(new PropertyValueFactory<>("path"));
        pathColumn.setPrefWidth(500);
        pathColumn.setResizable(true);
        pathColumn.getStyleClass().add("table-column");
        pathColumn.setMaxWidth(10000);  // 允许路径列在窗口拉伸时自动扩展

        // 大小列（固定宽度）
        TableColumn<FileResult, String> sizeColumn = new TableColumn<>("大小");
        sizeColumn.setCellValueFactory(new PropertyValueFactory<>("size"));
        sizeColumn.setPrefWidth(100);
        sizeColumn.setResizable(false);  // 大小列无需调整
        sizeColumn.getStyleClass().add("table-column");
        sizeColumn.setStyle("-fx-alignment: CENTER_RIGHT;");  // 右对齐更美观

        // 修改日期列（固定宽度）
        TableColumn<FileResult, String> modifiedColumn = new TableColumn<>("修改日期");
        modifiedColumn.setCellValueFactory(new PropertyValueFactory<>("lastModified"));
        modifiedColumn.setPrefWidth(160);
        modifiedColumn.setResizable(false);
        modifiedColumn.getStyleClass().add("table-column");
        modifiedColumn.setStyle("-fx-alignment: CENTER;");  // 居中对齐

        tableView.getColumns().addAll(nameColumn, typeColumn, pathColumn, sizeColumn, modifiedColumn);

        // 表格行工厂保持不变，但添加了样式类
        tableView.setRowFactory(tv -> {
            TableRow<FileResult> row = new TableRow<>();

            // 创建右键菜单
            ContextMenu contextMenu = new ContextMenu();
            contextMenu.getStyleClass().add("context-menu");

            // 打开文件菜单项
            MenuItem openFileMenuItem = new MenuItem("打开文件");
            openFileMenuItem.getStyleClass().add("menu-item");
            openFileMenuItem.setOnAction(event -> {
                if (!row.isEmpty()) {
                    openFile(row.getItem());
                }
            });

            // 打开文件夹菜单项
            MenuItem openFolderMenuItem = new MenuItem("文件夹中显示");
            openFolderMenuItem.getStyleClass().add("menu-item");
            openFolderMenuItem.setOnAction(event -> {
                if (!row.isEmpty()) {
                    openContainingFolder(row.getItem());
                }
            });

            // 复制路径菜单项
            MenuItem copyPathMenuItem = new MenuItem("复制文件路径");
            copyPathMenuItem.getStyleClass().add("menu-item");
            copyPathMenuItem.setOnAction(event -> {
                if (!row.isEmpty()) {
                    copyFilePathToClipboard(row.getItem());
                }
            });

            // 删除文件菜单项
            MenuItem deleteMenuItem = new MenuItem("删除");
            deleteMenuItem.getStyleClass().add("menu-item");
            deleteMenuItem.setOnAction(event -> {
                if (!row.isEmpty()) {
                    deleteFile(row.getItem());
                }
            });

            contextMenu.getItems().addAll(
                    openFileMenuItem,
                    openFolderMenuItem,
                    new SeparatorMenuItem(),
                    copyPathMenuItem,
                    deleteMenuItem
            );

            // 设置行的上下文菜单
            row.contextMenuProperty().bind(
                    Bindings.when(row.emptyProperty())
                            .then((ContextMenu)null)
                            .otherwise(contextMenu)
            );

            // 双击事件打开文件
            row.setOnMouseClicked(event -> {
                if (event.getClickCount() == 2 && !row.isEmpty()) {
                    openFile(row.getItem());
                }
            });

            return row;
        });

        return tableView;
    }

    private HBox createStatusPanel() {
        HBox statusPanel = new HBox(15);
        statusPanel.getStyleClass().add("status-panel");
        statusPanel.setPadding(new Insets(8));

        statusLabel.getStyleClass().add("status-label");
        statusLabel.setMinWidth(200); // 确保状态文本有足够空间


        progressBar.setPrefWidth(300);
        progressBar.setMinHeight(15);   // 最小高度（避免被压缩为0）
        progressBar.getStyleClass().add("progress-bar");
        statusPanel.getChildren().addAll(progressBar, statusLabel);

        return statusPanel;
    }

    private void startSearch() {
        String keyword = searchField.getText().trim().toLowerCase();
        if (keyword.isEmpty()) {
            statusLabel.setText("请输入搜索关键词");
            return;
        }
        if (isSearching.get()) return;

        // 初始化状态
        isSearching.set(true);
        fileResults.clear();
        statusLabel.setText("搜索中...");

        currentSearchTask = new Task<>() {
            @Override
            protected Void call() {
                File[] roots = File.listRoots();
                int total = roots.length;
                CountDownLatch latch = new CountDownLatch(total);

                for (File root : roots) {
                    executor.submit(() -> {
                        try {
                            Path rootPath = root.toPath();
                            // 跳过系统保护目录（提升效率）
                            if (isSystemDir(rootPath)) return;

                            Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
                                @Override
                                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                                    if (!isSearching.get()) return FileVisitResult.TERMINATE;
                                    if (file.getFileName().toString().toLowerCase().contains(keyword)) {
                                        addResult(file, attrs, false);
                                    }
                                    return FileVisitResult.CONTINUE;
                                }

                                @Override
                                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                                    if (!isSearching.get()) return FileVisitResult.TERMINATE;
                                    // 检查是否需要跳过该目录（包含异常处理）
                                    if (shouldSkipDirectory(dir)) {
                                        return FileVisitResult.SKIP_SUBTREE;
                                    }
                                    if (dir.getFileName() != null &&
                                            dir.getFileName().toString().toLowerCase().contains(keyword)) {
                                        addResult(dir, attrs, true); // 标记为目录
                                    }
                                    return FileVisitResult.CONTINUE;
                                }

                                @Override
                                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                                    // 忽略访问失败的文件/目录
                                    return FileVisitResult.CONTINUE;
                                }
                            });
                        } catch (Exception e) {
                        } finally {
                            latch.countDown();
                        }
                    });
                }

                try { latch.await(); } catch (InterruptedException ignored) {}
                Platform.runLater(() -> {
                    statusLabel.setText("搜索完成，找到 " + fileResults.size() + " 个结果");
                    updateProgress(total - latch.getCount(), total);
                    isSearching.set(false);
                });
                return null;
            }
        };

        progressBar.progressProperty().bind(currentSearchTask.progressProperty());
        new Thread(currentSearchTask).start();
    }

    /**
     * 是否需要跳过的目录
     * @param dir 目录对象
     * @return 是否需要跳过
     */
    private boolean shouldSkipDirectory(Path dir) {
        try {
            // 检查是否为系统保护目录
            String pathStr = dir.toString().toLowerCase();
            return pathStr.contains("system volume information")
                    || pathStr.contains("windows\\system32")
                    || pathStr.contains("$recycle.bin")
                    || pathStr.contains("program files")
                    || pathStr.contains("appdata\\local\\temp");

        } catch (Exception e) {
            // 发生异常时（如无权限），直接跳过该目录
            return true;
        }
    }

    /**
     * 判断是否为系统目录
     * @param path 目录对象
     * @return 是否为系统目录
     */
    private boolean isSystemDir(Path path) {
        String pathStr = path.toString().toLowerCase();
        return pathStr.contains("system volume information")
                || pathStr.contains("windows\\system32");
    }

    /**
     * 组装搜索结果对象
     * @param file 文件对象
     * @param attrs 文件属性信息
     */
    private void addResult(Path file, BasicFileAttributes attrs, boolean isDirectory) {
        try {
            LocalDateTime time = LocalDateTime.ofInstant(
                    attrs.lastModifiedTime().toInstant(), ZoneId.systemDefault()
            );
            // 目录大小显示为"-"，文件显示实际大小
            String size = isDirectory ? "-" : formatSize(attrs.size());
            // 名称前添加类型标识（文件夹/文件）
            String name = file.getFileName().toString();
            String type;
            if (isDirectory) {
                type = "📂 文件夹"; // 目录
            } else {
                // 根据文件扩展名判断类型
                String fileName = file.getFileName().toString().toLowerCase();
                if (fileName.endsWith(".xlsx") || fileName.endsWith(".xls")) {
                    type = "📊 Excel表格"; // Excel表格
                } else if (fileName.endsWith(".docx") || fileName.endsWith(".doc")) {
                    type = "📓 Word文档"; // Word文档
                } else if (fileName.endsWith(".pptx") || fileName.endsWith(".ppt")) {
                    type = "🖥️ PPT演示文稿"; // PPT演示文稿
                } else if (fileName.endsWith(".pdf")) {
                    type = "📙 PDF文件"; // PDF文件
                } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
                        fileName.endsWith(".png") || fileName.endsWith(".gif") ||
                        fileName.endsWith(".bmp") || fileName.endsWith(".webp")) {
                    type = "🎨 图片文件"; // 图片文件
                } else if (fileName.endsWith(".mp4") || fileName.endsWith(".avi") ||
                        fileName.endsWith(".mov") || fileName.endsWith(".mkv") ||
                        fileName.endsWith(".flv")) {
                    type = "🎬 视频文件"; // 视频文件
                } else if (fileName.endsWith(".mp3") || fileName.endsWith(".wav") ||
                        fileName.endsWith(".flac") || fileName.endsWith(".m4a")) {
                    type = "🎵 音频文件"; // 音频文件
                } else if (fileName.endsWith(".zip") || fileName.endsWith(".rar") ||
                        fileName.endsWith(".7z") || fileName.endsWith(".tar") ||
                        fileName.endsWith(".gz")) {
                    type = "📦 压缩文件"; // 压缩文件
                } else if (fileName.endsWith(".txt")) {
                    type = "📄 文本文件"; // 文本文件
                } else if (fileName.endsWith(".exe") || fileName.endsWith(".msi")) {
                    type = "🚀 可执行文件"; // 可执行文件
                } else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) {
                    type = "🌐 网页文件"; // 其他文件
                } else {
                    type = "📄 其他文件"; // 其他文件
                }
            }
            FileResult result = new FileResult(
                    type,
                    name,
                    file.toString(),
                    size,
                    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(time)
            );
            Platform.runLater(() -> fileResults.add(result));
        } catch (Exception ignored) {}
    }

    private void selectDirectory() {
        DirectoryChooser directoryChooser = new DirectoryChooser();
        directoryChooser.setTitle("选择要搜索的目录");
        File selectedDirectory = directoryChooser.showDialog(null);
        if (selectedDirectory != null) {
            // 实现自定义目录搜索逻辑
        }
    }

    private void openFile(FileResult fileResult) {
        try {
            // 使用纯JavaFX方式打开文件
            File file = new File(fileResult.getPath());
            String os = System.getProperty("os.name").toLowerCase();
            ProcessBuilder pb;

            if (os.contains("win")) {
                pb = new ProcessBuilder("cmd.exe", "/c", "start", "\"\"", file.getAbsolutePath());
            } else if (os.contains("mac")) {
                pb = new ProcessBuilder("open", file.getAbsolutePath());
            } else {
                pb = new ProcessBuilder("xdg-open", file.getAbsolutePath());
            }
            pb.start();
        } catch (IOException e) {
            e.printStackTrace();
            Alert alert = new Alert(Alert.AlertType.ERROR, "无法打开文件: " + fileResult.getPath());
            alert.getDialogPane().getStylesheets().add(Objects.requireNonNull(getClass().getResource("/styles.css")).toExternalForm());
            alert.showAndWait();
        }
    }

    private void openContainingFolder(FileResult fileResult) {
        try {
            String os = System.getProperty("os.name").toLowerCase();
            if(os.contains("win")) {
                // win下模拟打开文件所在路径的指令
                String command = "explorer.exe /select," + fileResult.getPath();
                new ProcessBuilder("cmd.exe", "/c", command).start();
            } else {
                File file = new File(fileResult.getPath());
                Desktop desktop = Desktop.getDesktop();
                desktop.open(file.getParentFile());
            }
        } catch (IOException e) {
            e.printStackTrace();
            Alert alert = new Alert(Alert.AlertType.ERROR, "文件夹中显示失败");
            alert.getDialogPane().getStylesheets().add(Objects.requireNonNull(getClass().getResource("/styles.css")).toExternalForm());
            alert.showAndWait();
        }
    }

    private void copyFilePathToClipboard(FileResult fileResult) {
        ClipboardContent content = new ClipboardContent();
        content.putString(fileResult.getPath());
        Clipboard.getSystemClipboard().setContent(content);
    }

    private void deleteFile(FileResult fileResult) {
        // 创建确认对话框
        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
        alert.setTitle("确认删除");
        alert.setContentText("确定要删除这个文件吗?\n" + fileResult.getName());
        alert.getDialogPane().getStylesheets().add(Objects.requireNonNull(getClass().getResource("/styles.css")).toExternalForm());

        Optional<ButtonType> result = alert.showAndWait();
        if (result.isPresent() && result.get() == ButtonType.OK){
            try {
                File file = new File(fileResult.getPath());
                if(file.exists()) {
                    if(file.delete()) {
                        // 从表格中移除
                        tableView.getItems().remove(fileResult);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                Alert errorAlert = new Alert(Alert.AlertType.ERROR, "删除文件失败: " + e.getMessage());
                errorAlert.getDialogPane().getStylesheets().add(Objects.requireNonNull(getClass().getResource("/styles.css")).toExternalForm());
                errorAlert.showAndWait();
            }
        }
    }

    private String formatSize(long size) {
        if (size < 1024) return size + " B";
        int exp = (int) (Math.log(size) / Math.log(1024));
        char pre = "KMGTPE".charAt(exp - 1);
        return String.format("%.1f %sB", size / Math.pow(1024, exp), pre);
    }

    @Override
    public void stop() {
        if (currentSearchTask != null) {
            currentSearchTask.cancel(true);
        }
    }

    public static class FileResult {
        private final String type;
        private final String name;
        private final String path;
        private final String size;
        private final String lastModified;

        public FileResult(String type, String name, String path, String size, String lastModified) {
            this.type = type;
            this.name = name;
            this.path = path;
            this.size = size;
            this.lastModified = lastModified;
        }

        public String getType() {
            return type;
        }

        public String getName() {
            return name;
        }

        public String getPath() {
            return path;
        }

        public String getSize() {
            return size;
        }

        public String getLastModified() {
            return lastModified;
        }
    }
}